package cool.webstudy.async.service.impl;

import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.fasterxml.jackson.databind.ObjectMapper;
import cool.webstudy.async.configuration.mybatisplus.DynamicTableNameHelper;
import cool.webstudy.common.constant.LoggerConstant;
import cool.webstudy.async.mapper.logger.api.ApiLoggerMapper;
import cool.webstudy.common.model.dto.logger.api.CreateApiLogDBDTO;
import cool.webstudy.common.model.dto.logger.api.CreateApiLogESDTO;
import cool.webstudy.common.model.po.logger.api.ApiLoggerPO;
import cool.webstudy.async.service.ApiLoggerService;
import cool.webstudy.common.utils.TimeUtil;
import cool.webstudy.common.utils.UUIDUtil;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Requests;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author think
 * @date 2024/8/1 23:33
 * @description: 接口请求日志管理接口实现类
 */
@Service
public class ApiLoggerServiceImpl implements ApiLoggerService {
    Logger businessLogger = Logger.getLogger(LoggerConstant.BUSINESS_LOGGER);
    @Autowired
    private ApiLoggerMapper apiLoggerMapper;
    @Autowired
    private RestHighLevelClient client;

    @Override
    public Boolean isExistTable(Date date) {
        String tableName = LoggerConstant.API_LOG_TABLE_NAME_PREFIX + TimeUtil.getDateFormatStrYYYYMMDD(date);
        String sql = String.format("SHOW TABLES LIKE '%s';", tableName);
        Object object = SqlRunner.db().selectObj(sql);
        if (Objects.nonNull(object)) {
            return true;
        }
        return false;
    }

    @Override
    public Boolean createTable(Date date) {
        String tableName = LoggerConstant.API_LOG_TABLE_NAME_PREFIX + TimeUtil.getDateFormatStrYYYYMMDD(date);
        String sql = String.format("CREATE TABLE IF NOT EXISTS `%s`  (\n" +
                "  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '表id',\n" +
                "  `un_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '业务主键',\n" +
                "  `trace_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '追踪id',\n" +
                "  `operator` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '操作人',\n" +
                "  `source_ip` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '来源ip',\n" +
                "  `order_id` int(11) NULL DEFAULT NULL COMMENT '顺序id',\n" +
                "  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '记录创建时间',\n" +
                "  `cost_time` int(10) UNSIGNED NULL DEFAULT NULL COMMENT '耗时（单位为ms）',\n" +
                "  `operational_status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'FAILURE：失败, SUCCESS：成功, 操作状态',\n" +
                "  `request_status` int(11) NULL DEFAULT NULL COMMENT '请求状态码',\n" +
                "  `api_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '接口路径',\n" +
                "  `api_action_method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '接口请求方式',\n" +
                "  PRIMARY KEY (`id`) USING BTREE\n" +
                ") ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '接口请求日志模板' ROW_FORMAT = Dynamic;\n", tableName);
        try {
            SqlRunner.db().update(sql);
        } catch (Exception e) {
            //TODO 通过日志记录工具将错误日志写入磁盘文件
            return false;
        }
        return true;
    }

    @Override
    public Boolean createApiLogToDB(CreateApiLogDBDTO dto) {
        if (Objects.isNull(dto)) {
            return true;
        }
        Boolean table = createTable(dto.getCreateTime());
        DynamicTableNameHelper.setTableName(LoggerConstant.API_LOG_TABLE_NAME_PREFIX + TimeUtil.getDateFormatStrYYYYMMDD(dto.getCreateTime()));
        ApiLoggerPO apiLoggerPO = getApiLoggerPO(dto);
        int insert;
        try {
            insert = apiLoggerMapper.insert(apiLoggerPO);
        } catch (Exception e) {
            insert = 0;
        }
        if (Boolean.FALSE.equals(table) || insert == 0) {
            //TODO 将日志写入磁盘日志文件中，在UTIL中创建保存日志文件至磁盘的工具类
            return false;
        }
        return true;
    }

    /**
     * @param date 日期
     * @return Boolean
     * @description: 判断日志索引是否存在
     */
    @Override
    public Boolean isExistIndex(Date date) {
        String indexName = LoggerConstant.API_LOG_INDEX_NAME_PREFIX + TimeUtil.getDateFormatStrYYYYMMDD(date);
        GetIndexRequest request = new GetIndexRequest(indexName);
        try {
            return client.indices().exists(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            //TODO 将日志写入磁盘日志文件中，在UTIL中创建保存日志文件至磁盘的工具类
            return false;
        }
    }

    /**
     * @param date 日期
     * @return Boolean
     * @description: 创建日志索引
     */
    @Override
    public Boolean createIndex(Date date) {
        String indexName = LoggerConstant.API_LOG_INDEX_NAME_PREFIX + TimeUtil.getDateFormatStrYYYYMMDD(date);
        GetIndexRequest request = new GetIndexRequest(indexName);
        //判断索引是否存在，如果存在则返回true，不存在则创建索引，并返回true
        try {
            boolean flagExist = client.indices().exists(request, RequestOptions.DEFAULT);
            if (flagExist) {
                return true;
            }
        } catch (Exception e) {
            //TODO 将日志写入磁盘日志文件中，在UTIL中创建保存日志文件至磁盘的工具类
            return false;
        }
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
        CreateIndexResponse createIndexResponse;
        try {
            createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        } catch (Exception e) {
            //TODO 将日志写入磁盘日志文件中，在UTIL中创建保存日志文件至磁盘的工具类
            return false;
        }
        if (!createIndexResponse.isAcknowledged()) {
            //没有创建索引成功，返回false
            //TODO 将日志写入磁盘日志文件中，在UTIL中创建保存日志文件至磁盘的工具类
            return false;
        }
        return true;
    }

    /**
     * @param dto
     * @return Boolean
     * @description: 记录接口请求记录日志至ES
     */
    @Override
    public Boolean createApiLogToES(CreateApiLogESDTO dto) {
        if (Objects.isNull(dto)) {
            return true;
        }
        String indexName = LoggerConstant.API_LOG_INDEX_NAME_PREFIX + TimeUtil.getDateFormatStrYYYYMMDD(dto.getCreateTime());
        try {
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(dto);
            IndexResponse response =  client.index(Requests.indexRequest(indexName).id(dto.getTraceId())
                    .source(json, XContentType.JSON)
                    .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT);
            return true ;
        } catch (Exception e){
            //在没有解决ES client api版本问题之前可通过该方式解决ES响应信息解析失败的情况--start
            String msg = e.getMessage();
            if(!msg.contains("201 Created") && !msg.contains("200 OK")) {
                businessLogger.log(Level.OFF,e.getMessage(),e.getStackTrace());
                return false;
            }
            //在没有解决ES client api版本问题之前可通过该方式解决ES响应信息解析失败的情况--end
            return true;

        }
    }

    private static ApiLoggerPO getApiLoggerPO(CreateApiLogDBDTO dto) {
        ApiLoggerPO apiLoggerPO = new ApiLoggerPO();
        apiLoggerPO.setUnCode(UUIDUtil.getUUID());
        apiLoggerPO.setTraceId(dto.getTraceId());
        apiLoggerPO.setOperator(dto.getOperator());
        apiLoggerPO.setSourceIp(dto.getSourceIp());
        apiLoggerPO.setOrderId(dto.getOrderId());
        apiLoggerPO.setCreateTime(dto.getCreateTime());
        apiLoggerPO.setCostTime(dto.getCostTime());
        apiLoggerPO.setOperationalStatus(dto.getOperationalStatus());
        apiLoggerPO.setRequestStatus(dto.getRequestStatus());
        apiLoggerPO.setApiPath(dto.getApiPath());
        apiLoggerPO.setApiActionMethod(dto.getApiActionMethod());
        return apiLoggerPO;
    }
}
